筆記目錄

Skip to content

使用 .NET 產生帶有浮水印的 Excel

TLDR

  • Excel 無內建浮水印功能,可透過設定「滿版透明背景圖片」模擬。
  • 浮水印在「標準模式」與「整頁模式」可透過 Background 設定;「整頁模式」與「列印」則透過 Header 設定。
  • 產生浮水印圖片時,需根據 Excel 的 PaperSize 調整圖片尺寸,橫向紙張需將寬高對調。
  • EPPlus 6 之後因移除 System.Drawing.Common 依賴,相關實作方式可能失效。
  • NPOI 可透過底層 API 操作 XML 結構(VML Drawing)來達成浮水印效果。

模擬浮水印的策略

Excel 本身並無浮水印功能,實務上可透過設定滿版背景圖片來達成。根據檢視模式的不同,設定方式如下:

  • 標準模式與整頁模式:在 Background 設定圖片。
  • 整頁模式與列印:在 Header 設定圖片。
  • 分頁模式:目前尚無有效方法。

產生滿版浮水印圖片

由於 Excel 的版面設定(頁面方向與大小)決定了背景圖片的尺寸,開發者需根據 PaperSize 調整圖片大小。

取得紙張大小

可透過 System.Drawing.Printing.PrinterSettings 取得各類紙張的 RawKind 與對應尺寸。

csharp
PrinterSettings settings = new PrinterSettings() {
    PrinterName = "Microsoft XPS Document Writer"
};
foreach (System.Drawing.Printing.PaperSize printerPaperSize in settings.PaperSizes) {
    // 取得紙張名稱與寬高
}

調整圖片尺寸

若圖片尺寸不符合頁面大小,需進行縮放或補白處理。

csharp
public Image ResizeImageBackgroundToFullPage(Image watermark, int width, int height){
    if (watermark.Width > width || watermark.Height > height) {
        using (Image image = ZoomOutImage(width, height)) {
            return ResizeImageBackgroundToFullPageInternal(width, height, image);
        }
    }
    return ResizeImageBackgroundToFullPageInternal(width, height, watermark);
}

private Image ResizeImageBackgroundToFullPageInternal(int pageWidth, int pageHeight, Image image) {
    Image bitmap = new Bitmap(pageWidth, pageHeight);
    using Graphics graphics = Graphics.FromImage(bitmap);
    graphics.Clear(Color.White);
    graphics.DrawImage(image, (pageWidth - image.Width) / 2, (pageHeight - image.Height) / 2);
    graphics.CompositingQuality = CompositingQuality.HighQuality;
    graphics.SmoothingMode = SmoothingMode.HighQuality;
    graphics.Save();
    return bitmap;
}

使用 EPPlus 產生浮水印

在支援 System.Drawing.Common 的舊版 EPPlus 中,可透過以下方式設定:

csharp
sheet.HeaderFooter.OddHeader.InsertPicture(watermark, PictureAlignment.Centered);
sheet.BackgroundImage.Image = watermark;

WARNING

此作法可能不適用於 EPPlus 6,因該版本已移除對 System.Drawing.Common 的依賴。

使用 NPOI 產生浮水印 (XLSX)

NPOI 並未提供直接設定浮水印的 API,需透過底層操作 VML (Vector Markup Language) 結構來達成。

實作 VML 繪圖物件

由於預設的 XSSFVMLDrawing 僅用於註解 (Comment),需自行定義類別以處理浮水印圖片。

csharp
private class VmlDrawing : POIXMLDocumentPart {
    public string PictureRelId { get; set; }
    public Image Image { get; set; }

    protected override void Commit() {
        PackagePart part = GetPackagePart();
        Stream @out = part.GetOutputStream();
        Write(@out);
        @out.Close();
    }

    private void Write(Stream stream) {
        float width = Image.Width * 72 / Image.HorizontalResolution;
        float height = Image.Height * 72 / Image.VerticalResolution;

        using StreamWriter sw = new(stream);
        // 寫入符合 Excel 格式的 VML XML 結構
        // 內容包含 v:shape 與 v:imagedata 關聯
    }
}

設定浮水印流程

  1. 將圖片轉換為 byte[] 並加入 Workbook。
  2. 建立 VmlDrawing 關係並關聯圖片。
  3. legacyDrawingHF 設定至 Worksheet。
csharp
// 設定 Header 浮水印範例
sheet.Header.Center = HeaderFooter.PICTURE_FIELD.sequence;
sheet.GetCTWorksheet().legacyDrawingHF = new CT_LegacyDrawing {
    id = sheet.GetRelationId(drawing)
};

異動歷程

  • 2023-02-24 初版文件建立。